/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "nat_writer.h"

#include <string>
#include <sys/types.h>
#include <cinttypes>

#include "hmfs_utils.h"
#include "mkfs_format.h"
#include "securec.h"
#include "device_manager.h"
#include "hmfs_io.h"
#include "hmfs_common.h"
#include "hmfs_quota.h"

namespace OHOS {
namespace Hmfs {

NatWriter::NatWriter(HmfsMkfs *mkfs) : mkfs_(mkfs)
{
}

int32_t NatWriter::Prepare()
{
    HmfsData& hmfsData = mkfs_->hmfsData_;
    CmdConfig& cfgPara = mkfs_->cfgPara_;
    struct SuperBlockData* superBlock = mkfs_->GetSuperBlockData();
    if (superBlock == nullptr) {
        return -1;
    }

    natBlock_ = std::make_unique<NatBlockOnDisk>();
    if (natBlock_ == nullptr) {
        HMFS_ERROR("not enough memory for nat block");
        return -1;
    }
    struct natBlockData* natBlock = &natBlock_->natBlock;
    memset_s(natBlock, sizeof(NatBlockOnDisk), 0, sizeof(NatBlockOnDisk));


    /* quota inode */
    uint32_t id = 1;
    for (int32_t qtype = 0; qtype < HMFS_MAX_QUOTAS; qtype++) {
        if (!((1 << qtype) & cfgPara.quotaBits)) {
            continue;
        }

        SetLeValue(natBlock->entries[GetLeValue(superBlock->qfInodeId[qtype])].blockId,
            hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_HOT_NODE] * BLOCKS_PER_SEGMENT + id++);
        natBlock->entries[GetLeValue(superBlock->qfInodeId[qtype])].inodeNo = superBlock->qfInodeId[qtype];
    }

    /* root inode */
    SetLeValue(natBlock->entries[hmfsData.rootInode].blockId,
        hmfsData.mainStartBlkId + hmfsData.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT);
    SetLeValue(natBlock->entries[hmfsData.rootInode].inodeNo, hmfsData.rootInode);

    /* update node nat */
    SetLeValue(natBlock->entries[hmfsData.nodeInode].blockId, 1);
    SetLeValue(natBlock->entries[hmfsData.nodeInode].inodeNo, hmfsData.nodeInode);

    /* update meta nat */
    SetLeValue(natBlock->entries[hmfsData.metaInode].blockId, 1);
    SetLeValue(natBlock->entries[hmfsData.metaInode].inodeNo, hmfsData.metaInode);

    return 0;
}

int32_t NatWriter::Write()
{
    HmfsData& hmfsData = mkfs_->hmfsData_;
    uint32_t segmentSize = BLOCKS_PER_SEGMENT * HMFS_BLOCK_SIZE;
    auto buf = std::make_unique<uint8_t[]>(segmentSize);
    if (buf == nullptr) {
        HMFS_ERROR("not enough memory for nat segment");
        return -1;
    }
    memset_s(buf.get(), segmentSize, 0, segmentSize);

    uint64_t offset = hmfsData.natStartBlkId * HMFS_BLOCK_SIZE;
    uint32_t segmentWriteCount = hmfsData.segmentCountInNAT / 2;
    for (uint32_t i = 0; i < segmentWriteCount; i++) {
        if (HmfsIo::GetInstance().DevFill(buf.get(), offset, segmentSize)) {
            HMFS_ERROR("failed to write nat segment");
            return -1;
        }
        offset += (segmentSize * 2);
    }

    uint64_t bolckId = hmfsData.natStartBlkId;
    HMFS_DEBUG("write nat root at block 0x%08" PRIx64 "", bolckId);
    if (HmfsIo::GetInstance().DevWriteBlock(natBlock_.get(), bolckId)) {
        HMFS_ERROR("failed to write nat block 0 to disk");
        return -1;
    }

    return 0;
}

} // namespace Hmfs
} // namespace OHOS
